#pragma warning(disable:4099)
#include <gtkmm.h>
#pragma warning(default:4099)

#include "RawImage.h"

namespace RawProject
{
    void RawImage::applyToneMapping(int factor)
    {
        int sfactor = factor * factor;

        double * pBuffer0 = new double[_smallWidth * _smallHeight];
        double * pBuffer1 = new double[_smallWidth * _smallHeight];

        memset(pBuffer0, 0, _smallWidth * _smallHeight * sizeof(double));
        memset(pBuffer1, 0, _smallWidth * _smallHeight * sizeof(double));

        int width = (*this)->get_width();
        int height = (*this)->get_height();
        int vLength = MAX(MIN(MAX(width / 2, height / 2) , factor / 2) / 5 + 1, 5);

        double * lut = new double[vLength];
        for(int i = 0 ; i < vLength ; i++)
            lut[i] = pow(2,-16 * i * i / ((double)sfactor/25.0f));

        bool terminateLoop = false;
        int r = 0;
        int c = 0;
        int offset = - vLength + 1;

        while(!terminateLoop)
        {
            int fIdx = c + offset;
            if(fIdx < 0 ) fIdx = abs(fIdx);
            else if (fIdx > _smallWidth - 1) fIdx = 2 * _smallWidth - 1 - offset - c;

            pBuffer0[r * _smallWidth + c] += lut[abs(offset)] * _imgLuminance[r * _smallWidth + fIdx];

            offset++;
            if(offset >= vLength - 1)
            {
                offset = - vLength + 1;
                c++;
                if(c >= _smallWidth)
                {
                    r++;c = 0;
                    if(r >= _smallHeight)
                    {
                        terminateLoop = true;
                        r = 0;
                    }
                }
            }
        }

        terminateLoop = false;

        double luMax = 0.0;

        while(!terminateLoop)
        {
            int fIdx = r + offset;
            if(fIdx < 0 ) fIdx = abs(fIdx);
            else if (fIdx > _smallHeight - 1) fIdx = 2 * _smallHeight - 1 - offset - r;

            pBuffer1[r * _smallWidth + c] += lut[abs(offset)] * pBuffer0[fIdx * _smallWidth + c];

            offset++;
            if(offset >= vLength - 1)
            {
                luMax = MAX(luMax, pBuffer1[r * _smallWidth + c]);

                offset = - vLength + 1;
                r++;
                if(r >= _smallHeight)
                {
                    c++;r = 0;
                    if(c >= _smallWidth)
                    {
                        terminateLoop = true;
                        c = 0;
                    }
                }
            }
        }

        delete[] pBuffer0;
        delete[] lut;

        union {unsigned char charData[2];unsigned short shortData;} sample;

        for(unsigned int i = 0 ; i < (unsigned) width * height ; i++)
        {
		    int r = i / width;
		    int c = i % width;

		    int sr_min = r / 5;
		    int sc_min = c / 5;

		    if(sr_min == (_smallHeight - 1))
                sr_min--;
		    if(sc_min == (_smallWidth - 1))
                sc_min--;

            int sr_max = sr_min + 1;
            int sc_max = sc_min + 1;

            double coeffr = (r / 5.0f) - sr_min;
            double coeffc = (c / 5.0f) - sc_min;

		    double lum = pBuffer1[sr_min * _smallWidth + sc_min] * (2 - (coeffr + coeffc))
                        + pBuffer1[sr_max * _smallWidth + sc_min] * ((1 - coeffc) + coeffr)
                        + pBuffer1[sr_min * _smallWidth + sc_max] * ((1 - coeffr) + coeffc)
                        + pBuffer1[sr_max * _smallWidth + sc_max] * (coeffc + coeffr);
            lum /= 4;

            double maxPix = MAX(MAX(_imgCopy16[3*i],_imgCopy16[3*i+1]),_imgCopy16[3*i+2]);

            double coeff = - (2.88/(65535.0*65535.0)) * maxPix * maxPix + (2.38/65535.0) * maxPix + 0.5;
            double power = ((log(luMax) / log(lum)) * (coeff) + (1 - coeff));

            double R = pow(_imgCopy16[3*i  ], power);
            double G = pow(_imgCopy16[3*i+1], power);
            double B = pow(_imgCopy16[3*i+2], power);

            sample.shortData = MIN(65535.0, R);
            _imageBuf8[3*i    ] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1]) : (sample.charData[1] == 255 ? sample.charData[1] : sample.charData[1] + 1);

            sample.shortData = MIN(65535.0, G);
            _imageBuf8[3*i + 1] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1]) : (sample.charData[1] == 255 ? sample.charData[1] : sample.charData[1] + 1);

            sample.shortData = MIN(65535.0, B);
            _imageBuf8[3*i + 2] = ( ((unsigned char)rand()) < (sample.charData[0]) ) ? (sample.charData[1]) : (sample.charData[1] == 255 ? sample.charData[1] : sample.charData[1] + 1);
        }

        delete[] pBuffer1;
    }
}
